﻿#pragma once

#include <iostream>
#include <sstream>
#include <filesystem>
#include <fstream>
#include <mutex>
#include <memory>
#include <thread>

namespace Local_Log
{
	//日志默认路径和文件名
	const std::string defaultpath = "C:\\Users\\Administrator\\Desktop\\CPP2\\9M\\log\\log";
	const std::string defaultname = "log.txt";

	//日志等级
	enum LogLever
	{
		DEBUG,
		INFO,
		WARNING,
		ERROR,
		FATAL
	};

	//日志等级转字符串
	std::string LogLeverToString(LogLever lever)
	{
		switch (lever)
		{
		case LogLever::DEBUG:
			return "DEBUG";
		case LogLever::INFO:
			return "INFO";
		case LogLever::WARNING:
			return "WARNING";
		case LogLever::ERROR:
			return "ERROR";
		case LogLever::FATAL:
			return "FATAL";
		}
	}

	//时间
	std::string GetCurrTime()
	{
		time_t nowtime = time(0);
		struct tm curr;
		localtime_s(&curr, &nowtime);
		std::stringstream sstime;
		sstime << curr.tm_year + 1900 << '-'
			<< curr.tm_mon << '-'
			<< curr.tm_mday << ' '
			<< curr.tm_hour << ':'
			<< curr.tm_min << ':'
			<< curr.tm_sec;
		return sstime.str();
	}

	//日志刷新策略的接口
	//strategy 策略
	struct LogStrategy
	{
		//virtual 虚拟的
		virtual ~LogStrategy() = default;
		//sync 同步
		// = 0; 表示纯虚函数，没有默认实现，由派生类重写
		//包含纯虚函数的类为抽象类，抽象类不能实例化对象，只能由派生类继承
		virtual void SyncLog(const std::string& message) = 0;
	};

	//显示器刷新类
	//console 控制台
	class ConsoleLogStrategy :public LogStrategy
	{
	public:
		virtual void SyncLog(const std::string& message)
		{
			std::lock_guard<std::mutex> lock(_mutex);
			std::cout << message << std::endl;
		}
		~ConsoleLogStrategy()
		{
			std::cout << "ConsoleLogStrategy Destroy" << std::endl;
		}
	private:
		std::mutex _mutex;
	};

	//文件刷新类
	class FileLogStrategy :public LogStrategy
	{
	public:
		FileLogStrategy(std::string path = defaultpath, std::string name = defaultname)
			:_logpath(path)
			, _logname(name)
		{
			std::lock_guard<std::mutex> lock(_mutex);
			try
			{
				if (std::filesystem::exists(_logpath))
				{
					return;
				}
				std::filesystem::create_directories(_logpath);
			}
			catch (std::filesystem::filesystem_error& e)
			{
				std::cout << e.what() << std::endl;
			}
		}
		virtual void SyncLog(const std::string& message)
		{
			std::lock_guard<std::mutex> lock(_mutex);
			std::string pathname = _logpath + '\\' + _logname;
			std::ofstream ofs(pathname.c_str(), std::ios_base::out | std::ios_base::app);
			if (!ofs.is_open())
			{
				std::cout << "Local_Log::FileLogStrategy::SyncLog::ofstream::open error" << std::endl;
				return;
			}
			ofs << message << std::endl;
			ofs.close();
		}
		~FileLogStrategy()
		{
			std::cout << "FileLogStrategy Destroy" << std::endl;
		}
	private:
		std::string _logpath;
		std::string _logname;
		std::mutex _mutex;
	};

	//日志类
	class LocalLogger
	{
	public:
		LocalLogger()
		{
			UseConsoleLogStrategy();
		}
		//刷新策略-文件刷新
		void UseFileLogStrategy()
		{
			_strategy = std::make_unique<FileLogStrategy>();
		}
		//刷新策略-显示器刷新
		void UseConsoleLogStrategy()
		{
			_strategy = std::make_unique<ConsoleLogStrategy>();
		}

		//生成日志的内部类
		class LogMessage
		{
		public:
			//RAII风格，构造的时候创建好日志头部
			LogMessage(LogLever lever, const std::string& filename, size_t line, LocalLogger& logger)
				:_nowtime(GetCurrTime()),
				_lever(lever),
				_id(std::this_thread::get_id()),
				_filename(filename),
				_line(line),
				_logger(logger)
			{
				std::stringstream ss;
				ss << "[" << _nowtime << "] "
					<< "[" << LogLeverToString(_lever) << "] "
					<< "[" << _id << "] "
					<< "[" << _filename << "] "
					<< "[" << _line << "] -";
				_loghead = ss.str();
			}
			//RAII风格，析构刷新
			~LogMessage()
			{
				if (_logger._strategy != nullptr)
				{
					_logger._strategy->SyncLog(_loghead);
				}
			}
			//重载 << 支持C++连续输出模式
			template<class T>
			LogMessage& operator <<(const T& t)
			{
				std::stringstream ss;
				//将t通过流提取为字符串
				ss << t;
				_loghead += ss.str();
				//支出连续的<<
				return *this;
			}
		private:
			std::string _nowtime;	//日志时间
			LogLever _lever;		//日志等级
			std::thread::id _id;	//日志ID
			std::string _filename;	//日志文件
			size_t _line;			//日志行号
			LocalLogger& _logger;	//外部日志类
			std::string _loghead;	//日志头部
		};
		//LogMessage(LogLever lever, std::string& filename, size_t line, LocalLogger& logger)
		// 故意拷⻉，形成LogMessage临时对象，后续在被<<时，会被持续引⽤，
		// 直到完成输⼊，才会⾃动析构临时LogMessage，⾄此也完成了⽇志的显⽰或者刷新
		// 同时，形成的临时对象内包含独⽴⽇志数据
		LogMessage operator()(LogLever lever, const std::string& filename, size_t line)
		{
			//返回匿名对象
			return LogMessage(lever, filename, line, *this);
		}
	private:
		std::unique_ptr<LogStrategy> _strategy;	//日志写入策略
	};

	// 定义全局的logger对象
	LocalLogger logger;
	// 使⽤宏，可以进⾏代码插⼊，⽅便随时获取⽂件名和⾏号
#define LOG(type) logger(type, __FILE__, __LINE__)
	// 使⽤宏，可以进⾏代码插⼊，⽅便随时获取⽂件名和⾏号
#define LOG(type) logger(type, __FILE__, __LINE__)
// 提供选择使⽤何种⽇志策略的⽅法
#define ENABLE_CONSOLE_LOG_STRATEGY() logger.UseConsoleLogStrategy()
#define ENABLE_FILE_LOG_STRATEGY() logger.UseFileStrategy()
}